---
sidebar_position: 4
---

# Пример создания иерархии классов, связанных наследованием

Пусть в базе данных вуза должна храниться информация обо всех студентах и преподавателях. Представлять все данные в одном классе не получится, поскольку для преподавателей нам понадобится хранить данные, которые для студента неприменимы, и наоборот.

## Создание базового класса

Для решения этой задачи создадим базовый класс `Human`, который будет описывать модель человека. В нем будут храниться в переменной `name` имя, фамилия и отчество.

```cpp
// Human.h
#include <cstring>
#include <iostream>

class Human {
 public:
  // Конструктор класса Human
  Human(const char* n) {
    name = new char[strlen(n) + 1];
    std::strcpy(name, n);
  }
  ~Human() { delete[] name; }
  Human(const Human& copy) {
    name = new char[strlen(copy.name) + 1];
    std::strcpy(this->name, copy.name);
  }
  Human& operator=(const Human& copy) {
    if (this != &copy) {
      delete[] name;
      name = new char[strlen(copy.name) + 1];
      std::strcpy(this->name, copy.name);
    }
    return *this;
  }
  void print() { std::cout << name << std::endl; }
  // Получение Ф.И.О. человека
  char* get_name() { return name; }

 private:
  char* name;  // фамилия имя отчество
};
```

## Наследование от базового класса

Теперь создайте новый класс `Student`, который будет наследником класса `Human`. В новом классе добавлены новое свойство `scores` - целочисленный массив оценок студента и метод `get_average_score()` для вычисления среднего балла студента.

```cpp
// Student.h
#include <iostream>

#include "Human.h"

const int size_score = 5;

class Student : public Human {
 public:
  // Конструктор класса Student
  Student(char* name, int* scores) : Human(name) {
    for (int i = 0; i < size_score; ++i) this->scores[i] = scores[i];
  }
  // Получение среднего балла студента
  float get_average_score() {
    // Сумма всех оценок студента
    unsigned int sum_scores = 0;
    // Средний балл
    float average_score;
    for (int i = 0; i < size_score; ++i) {
      sum_scores += this->scores[i];
    }
    average_score = (float)sum_scores / size_score;
    return average_score;
  }
  void print() {
    // Вывод имени студента (используется унаследованный метод класса Human)
    Human::print();
    // Вывод оценок студента
    std::cout << "Оценки: ";
    for (int i = 0; i < size_score; ++i) std::cout << scores[i] << ' ';
    std::cout << std::endl;
    // Вывод среднего балла студента
    std::cout << "Средний балл: " << get_average_score() << std::endl
              << std::endl;
  }

 private:
  // Оценки студента
  int scores[size_score];
};
```

Все публичные свойства и методы класса `Human` будут доступны в классе `Student`.

## Конструктор базового класса

Для того чтобы выполнить конструктор родительского класса для объекта `Student` (в нашем случае — это заполнение поля `name`), используется следующий синтаксис:

```cpp
// Конструктор класса Student
Student(<аргументы конструктора текущего класса>)
    : Human(<инициализация конструктора родительского класса>) {
  // инициализация конструктора текущего класса
}
```

В конструктор класса `Human` мы передаем фамилию, имя, отчество человека, которые сохраняются в экземпляре класса. Для класса `Student` нам необходимо задать еще и список оценок студента. Поэтому конструктор `Student` принимает все аргументы конструктора базового класса, а также дополнительные аргументы для расширения функционала: 

```cpp
Student(char* name, int* scores) : Human(name) {
  for (int i = 0; i < size_score; ++i) this->scores[i] = scores[i];
}
```

Список оценок студента хранится в массиве.

## Создание объекта класса `Student`

Реализуем пользовательский интерфейс для работы с классом `Student`.

```cpp
// main.cpp
#include <iostream>

#include "Student.h"

int main() {
  setlocale(LC_ALL, "Russian");
  // Оценки студента
  int scores[size_score] = {5, 4, 4, 5, 3};
  // Создание объекта класса Student
  Student* stud = new Student((char*)"Петров Иван Алексеевич", scores);
  stud->print();
  delete stud;
  return 0;
}
```

В этом примере мы написали программу, которая создает объект класса `Student`, сохраняя в нем его имя, фамилию, отчество и список оценок.
После инициализации объекта происходит вывод всех данных о студенте. Для этого для объекта вызывается метод `print()`, в котором:

- вызывается метод `print()`, унаследованный от базового класса `Human` – выводится имя объекта;
- выводятся оценки студента – вывод содержимого переменной-члена  `scores`;
- затем в методе вычисляется средний балл студента (вызов метода `get_average_score`) и выводится на экран.

Результат выполнения `main()`:

```shell
Петров Иван Алексеевич
Оценки: 5 4 4 5 3 
Средний балл: 4.2
```

Мы реализовали часть функционала для нашей базы данных института (мы, конечно, утрируем, когда оперируем столь серьезными высказываниями про настоящую базу данных).

## Создание класса-наследника `Teacher`

Нужно создать еще один класс, в котором будут храниться данные преподавателей. Дадим ему название — `Teacher`. Как вы уже поняли, мы не будем описывать все методы этого класса с нуля, а просто унаследуем его от класса `Human`. Тогда не нужно будет реализовывать хранение имени, фамилии и отчества преподавателя. Это уже есть в базовом классе `Human`.

```cpp
// Teacher.h
#include <iostream>

#include "Human.h"

class Teacher : public Human {
 public:
  // Конструктор класса Student
  Teacher(const char* name,
          // Количество учебных часов за семестр у преподавателя
          unsigned int work_time)
      : Human(name) {
    this->work_time = work_time;
  }
  // Получение количества учебных часов
  unsigned int get_work_time() { return this->work_time; }
  void print() {
    // Вывод фамилии, имени, отчества преподавателя
    //(используется унаследованный метод класса Human)
    Human::print();
    // Вывод количества учебных часов преподавателя
    std::cout << "Количество часов: ";
    std::cout << work_time << std::endl << std::endl;
  }

 private:
  // Учебные часы
  unsigned int work_time;
};
```

У класса `Teacher` появилось новое свойство — количество учебных часов, отведенное преподавателю на единицу времени (семестр). Весь остальной функционал наследуется от базового класса `Human`. Если бы мы писали все с нуля, то одинакового кода получилось бы в разы больше, и его поддержка усложнилась бы на порядок.

Создание объекта класса `Teacher`

Изменим содержимое файла `main.cpp`, чтобы проверить работу класса `Teacher`.

```cpp
// main.cpp
#include <iostream>

#include "Teacher.h"

int main() {
  setlocale(LC_ALL, "Russian");
  // Количество учебных часов преподавателя
  unsigned int teacher_work_time = 40;
  Teacher* tch =
      new Teacher((char*)"Bacильков Петр Сергеевич", teacher_work_time);
  tch->print();
  delete tch;
  return 0;
}
```

Результат выполнения `main()`:

```shell
Bacильков Петр Сергеевич
Количество часов: 40
```

Можно таким же образом создать класс, в котором будут храниться данные обслуживающего персонала или руководящего состава. Наследование используют, когда у каждой группы объектов есть общие параметры, но для каждой из этих групп нужно хранить более кастомные данные.

Также мы можем создать класс, который будет описывать студента заочной формы обучения. Его мы унаследовали бы от класса `Student`, добавив кое-какие дополнительные данные.

В класс `Human` можно добавить еще больше свойств, которые будут описывать данные, имеющиеся у любого человека. Например, номер паспорта, дату рождения, прописку и место проживания.

Подобный подход позволяет в разы уменьшить дублирование кода в реальных проектах и упросить его поддержку.
